/*
 * Copyright 2012 Marsrutai.lt
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package lt.marsrutai.mobile.android.widget;

import lt.marsrutai.mobile.android.R;
import lt.marsrutai.mobile.android.fragment.PointSelectionDialogFragment;
import android.content.Context;
import android.location.Location;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.Editable;
import android.text.Spannable;
import android.text.TextWatcher;
import android.text.style.ForegroundColorSpan;
import android.util.AttributeSet;
import android.widget.AutoCompleteTextView;

/**
 * <p>
 * {@link AutoCompleteTextView} which can represent {@link Location} values.
 * </p>
 * <p>
 * Used in conjunction with {@link PointSelectionDialogFragment}. Whenever a
 * {@link DialogChoiceItem} is selected, it is stored in {@link #selectedItem},
 * to indicate that the actual value of this view is no longer plain text, but a
 * {@link Location}.
 * </p>
 * <p>
 * The value of {@link #location} will usually be set separately from the
 * {@link #selectedItem}. Whenever {@link #selectedItem} is set, the contents of
 * this view will be highlighted with a different color - to indicate a special
 * meaning. Any changes to the contents of this view will reset
 * {@link #selectedItem} to <code>null</code> and remove the highlight.
 * </p>
 * 
 * @author Justas
 */
public class AutoCompleteLocationTextView extends AutoCompleteTextView {

    /** See {@link ForegroundColorSpanRemover} */
    private ForegroundColorSpanRemover listener = new ForegroundColorSpanRemover();
    
    /** Value represented by the selectedItem */
    private Location location;
    
    /** Currently selected dialog item */
    private DialogChoiceItem selectedItem;

    public AutoCompleteLocationTextView(Context context) {
        this(context, null);
    }

    public AutoCompleteLocationTextView(Context context, AttributeSet attrs) {
        this(context, attrs, android.R.attr.autoCompleteTextViewStyle);
    }

    public AutoCompleteLocationTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        
        addTextChangedListener(listener);
    }
    
    @Override
    public Parcelable onSaveInstanceState() {
        Parcelable superState = super.onSaveInstanceState();
        SavedState state = new SavedState();
        state.superState = superState;
        state.selectedItem = selectedItem;
        state.location = location;
        
        return state;
    }
    
    @Override
    public void onRestoreInstanceState(Parcelable state) {
        SavedState savedState = (SavedState) state;

        // Need to pause listener - restoring the state modifies text, which in turn fires text changed events
        listener.pause();
        super.onRestoreInstanceState(savedState.superState);
        listener.resume();
        
        selectedItem = savedState.selectedItem;
        location = savedState.location;
    }
    
    /**
     * @return Location represented by this view
     */
    public Location getLocation() {
        return location;
    }
    
    /**
     * Sets the location represented by this view
     * 
     * @param location
     */
    public void setLocation(Location location) {
        this.location = location;
    }

    /**
     * Sets the currently selected choice
     * 
     * @param chosenItem
     */
    public void setSelectedChoice(DialogChoiceItem chosenItem) {
        if (chosenItem == null) {
            return;
        }
        
        updateTextualValue(chosenItem);
        addSelectedItem(chosenItem);
    }

    /**
     * @return Currently selected choice
     */
    public DialogChoiceItem getSelectedChoice() {
        return selectedItem;
    }

    /**
     * Removes the currently selected choice
     */
    public void removeSelectedChoice() {
        removeSelectedItem();
        setText(null);
    }

    /**
     * Checks whether currently selected item is the same as the provided item
     * 
     * @param item
     * @return
     */
    public boolean isSelected(DialogChoiceItem item) {
        if (item == null) {
            return false;
        }
        
        return item.equals(selectedItem);
    }

    /**
     * Removes the tagged {@link DialogChoiceItem}
     * 
     * @param textView
     */
    protected void removeSelectedItem() {
        if (selectedItem != null) {
            selectedItem.decUsedCount();
            selectedItem = null;
        }
    }

    /**
     * Adds a {@link DialogChoiceItem} tag to the view
     * 
     * @param textView
     * @param id
     */
    protected void addSelectedItem(DialogChoiceItem item) {
        // If a previously stored item exists - remove it first
        if (selectedItem != null) {
            removeSelectedItem();
        }

        selectedItem = item;
        selectedItem.incUsedCount();
    }

    /**
     * <p>
     * Sets text from the dialog selection and highlights it
     * </p>
     * 
     * @param listener
     * @param textView
     * @param id
     */
    protected void updateTextualValue(DialogChoiceItem chosenItem) {
        String string = getContext().getString(chosenItem.getNameResourceId());
        
        // Highlight the contents of the textView
        Spannable spannable = Spannable.Factory.getInstance().newSpannable(string);
        ForegroundColorSpan span = new ForegroundColorSpan(getContext().getResources().getColor(R.color.textHighlightForeground));
        spannable.setSpan(span, 0, string.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);

        // Pause the listener. The spannable that was just set would be cleared otherwise
        listener.pause();
        setText(spannable);
        listener.resume();
    }

    /**
     * <p>
     * Removes {@link ForegroundColorSpan} from the editable that generates text
     * changed events.
     * </p>
     * <p>
     * It's purpose is to clear the highlight from the text when the user
     * modifies text contents. Highlighted text indicates that the value is
     * special, and cannot be entered with a keyboard - it must be selected from
     * a dialog. Therefore if the special value is modified, it no longer is
     * special - highlight must be removed.
     * </p>
     * <p>
     * Additionally, this listener manages shared
     * {@link DialogChoiceItem} options: when the highlight is
     * removed, such options are marked as no longer used.
     * </p>
     * 
     * @author Justas
     */
    private class ForegroundColorSpanRemover implements TextWatcher {
        
        private boolean paused = false;

        private void pause() {
            paused = true;
        }
        
        private void resume() {
            paused = false;
        }
        
        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
        }
        
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        }
        
        @Override
        public void afterTextChanged(Editable s) {
            if (paused) {
                return;
            }

            ForegroundColorSpan[] spans = s.getSpans(0, s.length(), ForegroundColorSpan.class);
            
            if (spans != null) {
                removeSelectedItem();
                // text no longer is "special" - null the value
                setLocation(null);
                
                for (ForegroundColorSpan foregroundColorSpan : spans) {
                    s.removeSpan(foregroundColorSpan);
                }
            }
        }
    }
    
    public static class SavedState implements Parcelable {

        public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {

            @Override
            public SavedState createFromParcel(Parcel in) {
                return new SavedState(in);
            }

            @Override
            public SavedState[] newArray(int size) {
                return new SavedState[size];
            }
        };

        private Parcelable superState;
        private DialogChoiceItem selectedItem;
        private Location location;

        public SavedState() {
        }

        public SavedState(Parcel in) {
            superState = in.readParcelable(this.getClass().getClassLoader());
            selectedItem = in.readParcelable(this.getClass().getClassLoader());
            location = in.readParcelable(this.getClass().getClassLoader());
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeParcelable(superState, flags);
            dest.writeParcelable(selectedItem, flags);
            dest.writeParcelable(location, flags);
        }

        @Override
        public int describeContents() {
            return 0;
        }

    }

}
